// -----------------------------------------------------------------------
//  <copyright file="DaprIntegrationEventBus.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/20 15:00</last-date>
// -----------------------------------------------------------------------

using System.Reflection;

using DaprPlus.Dapr.Properties;
using DaprPlus.Domain.Events;

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;


namespace DaprPlus.Dapr.Events;

/// <summary>
/// 集成事件事件总线dapr实现
/// 强类型的集成事件，直接发布
/// 未知类型的集成事件，使用反射调用dapr.PublishEventAsync执行
/// </summary>
/// <param name="options"></param>
/// <param name="dapr"></param>
/// <param name="logger"></param>
public class DaprIntegrationEventBus(IOptions<DaprOptions> options, DaprClient dapr, ILogger<DaprIntegrationEventBus> logger)
    : IIntegrationEventBus
{
    private readonly Dictionary<Type, MethodInfo> _publishEventAsyncMethods = new();

    public virtual async Task PublishAsync<TIntegrationEvent>(TIntegrationEvent @event, CancellationToken token = default)
        where TIntegrationEvent : IntegrationEvent
    {
        var opts = options.Value;
        var pubSubName = opts.PubSubName;
        var fullName = @event.GetType().Name;
        if (string.IsNullOrEmpty(fullName))
        {
            return;
        }
        var topicName = fullName.Replace("IntegrationEvent", string.Empty).Replace("Event", string.Empty);
        if (@event.TargetService != null)
        {
            topicName = $"{@event.TargetService}_{topicName}";
        }

        logger.LogInformation(ResDapr.Format_EventBus_Publish, @event, pubSubName, topicName);

        await dapr.PublishEventAsync(pubSubName, topicName, @event, token);
    }

    public async Task PublishAsync(IntegrationEvent @event, CancellationToken token = default)
    {
        var opts = options.Value;
        var pubSubName = opts.PubSubName;
        var fullName = @event.GetType().Name;
        if (string.IsNullOrEmpty(fullName))
        {
            return;
        }
        var topicName = fullName.Replace("IntegrationEvent", string.Empty).Replace("Event", string.Empty);
        if (@event.TargetService != null)
        {
            topicName = $"{@event.TargetService}_{topicName}";
        }

        var eventType = @event.GetType();
        var method = GetPublishEventAsyncMethod(eventType);
        if (method == null)
        {
            return;
        }
        var task = method.Invoke(dapr, [pubSubName, topicName, @event, token]) as Task;
        if (task == null)
        {
            return;
        }

        await task;

        logger.LogInformation(ResDapr.Format_EventBus_Publish, @event.ToTextJson()?[..100], pubSubName, topicName);
    }

    /// <summary>
    /// 构建通用消息类型的 dapr.PublishEventAsync 泛型方法
    /// </summary>
    /// <param name="eventType"></param>
    /// <returns></returns>
    private MethodInfo? GetPublishEventAsyncMethod(Type eventType)
    {
        if (_publishEventAsyncMethods.TryGetValue(eventType, out var cacheMethod))
        {
            return cacheMethod;
        }
        var methods = typeof(DaprClient).GetMethods(BindingFlags.Public | BindingFlags.Instance)
            .Where(m => m is { Name: nameof(DaprClient.PublishEventAsync), IsGenericMethod: true });
        foreach (var method in methods)
        {
            var parameters = method.GetParameters();
            if (parameters.Length != 4
               || parameters[0].Name != "pubsubName"
               || parameters[1].Name != "topicName"
               || parameters[2].Name != "data"
               || parameters[3].Name != "cancellationToken")
            {
                continue;
            }
            var genericMethod = method.MakeGenericMethod(eventType);
            _publishEventAsyncMethods[eventType] = genericMethod;
            return genericMethod;
        }
        return null;
    }
}
